package chan.project.aimodule.Service;

import chan.project.smartlawmodel.dto.ai.DailyConsultationVo;
import chan.project.smartlawmodel.dto.common.LawSuit;
import chan.project.smartlawmodel.dto.data.LegalCase;
import chan.project.smartlawmodel.entity.Instrument;
import chan.project.smartlawmodel.entity.LawRegulation;

import chan.project.smartlawservice.dataManage.DataManageFeignClient;
import cn.hutool.core.util.ObjectUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 日常法律咨询接口
 */
@Component
@Slf4j
public class DailyConsultationService {

    @Resource
    private OpenAiEmbeddingModel openAiEmbeddingModel;

    @Resource
    private ResourcePatternResolver resourcePatternResolver;

    @Resource
    private DataManageFeignClient dataManageFeignClient;

    @Resource
    private ChatClient chatClient;

    private final String SystemMessage = "你是一个法律咨询专家，你需要根据用户的问题给出一个法律咨询建议。";

    private final String LawSystemMessage = "你是一个法律专家，你需要从用户的提问中获取出需要查阅的法律资料列表";

    private final String LitigationSimulatorMessage = """
    你是一个法律案件模拟器，旨在协助进行法律分析。请始终以以下确切的 JSON 结构返回响应，确保包含所有字段并正确格式化：
    {
        "winProbability": 胜诉概率的 double 类型值，以百分比表示（范围在 0.0 到 100.0 之间，例如 75.5），
        "estimatedTrialMonths": 预计审理时间的 integer 类型值，以月为单位（例如 6），
        "caseAnalysis": "案件的详细分析字符串，包含影响结果的关键因素",
        "historicalWinRate": 类似案件历史胜诉率的 double 类型值，以百分比表示（范围在 0.0 到 100.0 之间，例如 60.0），
    }
    请勿在 JSON 对象之外包含任何文本、解释或格式化内容。确保所有字段都存在，即使某些数组为空（例如 "literatures": []）。
""";
    /**
     * 日常咨询法律问题
     * @param message
     * @return
     */
    public DailyConsultationVo askQuestion(String message) {
        //1.采用AI获取需要查询的对应法律进行返回
        List<LawRegulation> list = messageToLaw(message);
        log.info("searchLawList:{}",list);
        //2.将法律信息进行分块
        List<Document> documents = new ArrayList<>();
        for (LawRegulation lawRegulation : list) {
            Document build = Document.builder()
                    .text(lawRegulation.getContent())
                    .build();
            //如果文档的token数量超过7000就会将其分块
            //参数意义为每个文本块的目标大小，每个文本块的最小大小，要包含的chunk的最小长度，从文本在生成的最大快数，是否再块中保留分隔符
            TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(8000, 200, 5, 10000, true);
            documents.addAll(tokenTextSplitter.split(build));
        }

        log.info("document:{}",documents);
        //传入向量并调用接口
        //指定向量模型
        VectorStore vectorStore = SimpleVectorStore.builder(openAiEmbeddingModel)
                .build();
        vectorStore.add(documents);
        ChatResponse chatResponse = chatClient.prompt()
                .user(message)
                .system(SystemMessage)
                .advisors(new QuestionAnswerAdvisor(vectorStore))
                .call()
                .chatResponse();
        String context = chatResponse.getResult().getOutput().getText();
        log.info("context: {}", context);
        DailyConsultationVo dailyConsultationVo = new DailyConsultationVo();
        dailyConsultationVo.setContent(context);
        List<String> collect = list.stream().map(LawRegulation::getTitle).collect(Collectors.toList());
        dailyConsultationVo.setUseLaws(collect);
        return dailyConsultationVo;
    }


    /**
     * 根据用户问题获取需要进行检索的法律
     * @param message
     */
    public List<LawRegulation> messageToLaw(String message){
        //指定向量模型
        VectorStore vectorStore = SimpleVectorStore.builder(openAiEmbeddingModel)
                .build();
        //1.获取系统中存储法律的名称
        List<String> allLawFileName = dataManageFeignClient.getAllLawFileName();
        //2.传入所有法律名称给AI进行解析，解析之后结构化输出为一个列表
        // 将法律法规进行处理，直接变为一个文档之后再进行分割
        Document build = Document.builder().text(allLawFileName.toString()).build();
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(8000, 200, 5, 10000, true);
        List<Document> documentArrayList = tokenTextSplitter.split(build);
        vectorStore.add(documentArrayList);
        List<String> entity = chatClient.prompt()
                .user(message)
                .system(LawSystemMessage)
                .advisors(new QuestionAnswerAdvisor(vectorStore))
                .call()
                .entity(new ParameterizedTypeReference<List<String>>() {
                });
        //3.从数据库中拿出所有的法律法规
        log.info("获取的法律名称:{}",entity);
        //注意如果数据库中匹配不到对应的法律法规可能是还未存储，因为此时为测试环境或者是新增法律未上台
        //此时可以给一个名称即可
        List<LawRegulation> collect = null;
        if (entity != null) {
            collect = entity.stream()
                    .map(lawName -> Optional.ofNullable(dataManageFeignClient.getLastLaw(lawName))
                            .orElseGet(() -> new LawRegulation(lawName)))
                    .collect(Collectors.toList());
        }
        return collect;
    }

    /**
     * 根据参考法律列表与参考案件列表进行诉讼模拟器
     * @param lawRegulations
     * @param allLegalCase
     * @return
     */
    public LawSuit litigationSimulator(LegalCase legalCase, List<LawRegulation> lawRegulations, List<Instrument> allLegalCase){
        List<Document> documents = new ArrayList<>();
        //将法律信息进行分块
        for (LawRegulation lawRegulation : lawRegulations) {
            if(ObjectUtil.isEmpty(lawRegulation)){
                break;
            }
            String content = lawRegulation.getContent();
            content = StringUtils.defaultIfBlank(content,"默认文本");
            Document build = Document.builder()
                    .text(content)
                    .build();
            TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(8000, 200, 5, 10000, true);
            documents.addAll(tokenTextSplitter.split(build));
        }
        //将案件进行分块
        for (Instrument instrument : allLegalCase) {
            if(ObjectUtil.isEmpty(instrument)){
                break;
            }
            Document build = Document.builder()
                    .text(instrument.getContent())
                    .build();
            TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(8000, 200, 5, 10000, true);
            documents.addAll(tokenTextSplitter.split(build));
        }
        VectorStore vectorStore = SimpleVectorStore.builder(openAiEmbeddingModel)
                .build();
        vectorStore.add(documents);
        LawSuit entity = chatClient.prompt()
                .user(legalCase.toString())
                .system(LitigationSimulatorMessage)
                .advisors(new QuestionAnswerAdvisor(vectorStore))
                .call()
                .entity(LawSuit.class);
        return entity;
    }

    /**
     * 读取多个文件并将文件分块(直接读取文件而不是通过搜索引擎)
     * @param list
     * @return
     */
    @Deprecated
    public List<Document> resourceToListDocument(List<String> list){
        List<Document> documents = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            org.springframework.core.io.Resource resource = resourcePatternResolver.getResource("classpath:knowledges/" + list.get(i) + ".docx");
            TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(resource);
            //如果文档的token数量超过7000就会将其分块
            //参数意义为每个文本块的目标大小，每个文本块的最小大小，要包含的chunk的最小长度，从文本在生成的最大快数，是否再块中保留分隔符
            TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(8000,200,5,10000,true);
            documents.addAll(tokenTextSplitter.apply(tikaDocumentReader.get()));
        }
        return documents;
    }
}
